package top.lingkang.finalmybatisextend;

import org.apache.ibatis.builder.SqlSourceBuilder;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.mapping.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import top.lingkang.finalmybatisextend.base.ParamObject;
import top.lingkang.finalmybatisextend.entity.SqlResult;
import top.lingkang.finalmybatisextend.log.NoLoggerImpl;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * @author lingkang
 * Created by 2022/10/10
 */
abstract class AbstractBaseExtend {
    protected static Logger log;
    protected List<Configuration> configuration = new ArrayList<>();
    protected FinalExtendConfig extendConfig;
    // 动态sql配置
    private static final Configuration sqlConfiguration = new Configuration();

    public AbstractBaseExtend(FinalExtendConfig extendConfig) {
        this.extendConfig = extendConfig;
        if (extendConfig.isShowSql())
            log = LoggerFactory.getLogger(getClass());
        else
            log = new NoLoggerImpl();

        if (extendConfig.getXml() == null || extendConfig.getXml().length == 0) {
            log.warn("未配置mapper.xml");
        } else {
            for (InputStream xml : extendConfig.getXml()) {
                Configuration config = new Configuration();
                new XMLMapperBuilder(xml, config, null, null).parse();
                configuration.add(config);
                try {
                    xml.close();
                } catch (IOException ignored) {
                }
            }
        }

    }

    public FinalExtendConfig getExtendConfig() {
        return extendConfig;
    }

    private List<Object> getParam(BoundSql boundSql, MappedStatement mappedStatement, Configuration configuration, ParamObject paramObject) {
        List<Object> params = new ArrayList<>();
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        TypeHandlerRegistry typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
        for (ParameterMapping parameterMapping : parameterMappings) {
            if (parameterMapping.getMode() != ParameterMode.OUT) {
                Object value;
                String propertyName = parameterMapping.getProperty();
                if (boundSql.hasAdditionalParameter(propertyName)) {
                    value = boundSql.getAdditionalParameter(propertyName);
                } else if (paramObject == null) {
                    value = null;
                } else if (typeHandlerRegistry.hasTypeHandler(paramObject.getClass())) {
                    value = paramObject;
                } else {
                    MetaObject metaObject = configuration.newMetaObject(paramObject);
                    value = metaObject.getValue(propertyName);
                }
                params.add(value);
            }
        }
        if ((paramObject == null || paramObject.isEmpty()) && !params.isEmpty()) {
            throw new IllegalArgumentException("解析xml入参不匹配，xml需要的参数变量数：" + params.size() + "   入参：" + paramObject);
        }
        return params;
    }

    public SqlResult get(String id) {
        return get(id, null);
    }

    public SqlResult get(String id, ParamObject paramObject) {
        SqlResult sqlResult = new SqlResult();
        Configuration config = getConfiguration(id);
        MappedStatement mappedStatement = config.getMappedStatement(id);
        BoundSql boundSql = mappedStatement.getBoundSql(paramObject);
        sqlResult.setSql(boundSql.getSql());
        sqlResult.setParams(getParam(boundSql, mappedStatement, config, paramObject));
        if (extendConfig.isShowSql()) {
            log.info("\n\nsql:\n{}\nparam:\n{}\n", sqlResult.getSql(), sqlResult.getParams());
        }
        return sqlResult;
    }

    public Configuration getConfiguration(String id) {
        for (Configuration config : configuration) {
            if (config.hasStatement(id, false))
                return config;
        }
        throw new IllegalArgumentException("xml中未找到映射id：" + id);
    }

    /**
     * @param mybatisSql  mybatis的sql
     * @param paramObject 入参
     * @return
     */
    public SqlResult sqlToSqlResult(String mybatisSql, ParamObject paramObject) {
        SqlSource sqlSource = new SqlSourceBuilder(sqlConfiguration).parse(
                mybatisSql,
                HashMap.class, // 参数类型
                null
        );

        BoundSql boundSql = sqlSource.getBoundSql(paramObject);
        return new SqlResult(boundSql.getSql(), getSqlParam(boundSql, paramObject));
    }

    private List<Object> getSqlParam(BoundSql boundSql, ParamObject paramObject) {
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        if (parameterMappings.size() < paramObject.size())
            throw new IllegalArgumentException("sql需要参数个数与入参个数不一致: 需要个数：" + parameterMappings.size()
                    + " 入参个数：" + paramObject.size());
        List<Object> list = new ArrayList<>(parameterMappings.size());
        for (ParameterMapping parameterMapping : parameterMappings) {
            String property = parameterMapping.getProperty();
            list.add(paramObject.get(property));
        }
        return list;
    }

}
